%%
%% 负责主模块的工作，对外调用的程序员而言，它是唯一需要了解的模块
%%

-module(lib_chan).

-export([cast/2, start_server/0, start_server/1, connect/5, disconnect/1, rpc/2]).

-import(lists, [map/2, member/2, foreach/2]).
-import(lib_chan_mm, [send/2, close/1]).

%% -------------------------------------------------------------
%% Server Code

start_server() ->
	case os:getenv("HOME") of
		false ->
			exit({ebadenv, "HOME"});
		Home ->
			start_server(Home ++ "/.erlang_config/lib_chan.conf")	% 传进去配置文件的绝对路径
	end.

%% read configuration file - check syntax
%% call start_port_instance(Port, ConfigData)
%% where Port is the required Port and ConfigData
%% contines the configuration data
start_server(ConfigFile) ->
	% ConfigFile是文件的绝对路径
	% 文件内容大致如下：
	% {port, 1233}.
	% {service, xxx, password, xxxxx, mfa, xx,xx,xx}.
	io:format("lib_chan starting:~p~n", [ConfigFile]),
	% file:consult()之后返回的结果：{ok,Terms} | {error, Reason} 其中Terms是一个列表,大概是这种形式[{},{}]
	case file:consult(ConfigFile) of
		{ok, ConfigData} ->
			io:format("ConfigData=~p~n",[ConfigData]),
			case check_terms(ConfigData) of
				[] -> 
					start_server1(ConfigData);
				Errors ->
					exit({eDeaemonConfig, Errors})
			end;
		{error, Reason} ->
			exit({eDeaemonConfig, Reason})
	end.

check_terms(ConfigData) ->
	% map(Fun, List1) -> List2
	% 将List1中的每个元素去在Fun中执行，然后返回一个元素，最后返回的这些元素组成一个列表，
	% 返回给List2,
	% 例子：
	% lists:map(fun(X)->[X,X] end, [a,b,c]).
	% 结果：[[a,a],[b,b],[c,c]]
	L = map(fun check_term/1, ConfigData), % 正常回返回{ok, ok}
	[X || {error, X} <- L].		% 这一句是仅对错误处理而言的，如果解析错误，就会产生消息{error, {badTerm, X}}

check_term({port, P}) when is_integer(P) -> ok;
check_term({service, _, password, _, mfa, _,_,_}) -> ok;
check_term(X) -> {error, {badTerm, X}}. 

start_server1(ConfigData) ->
	register(lib_chan, spawn(fun() -> start_server2(ConfigData) end)).

start_server2(ConfigData) ->
	[Port] = [P || {port, P} <- ConfigData],	% 提取出了端口Port
	start_port_server(Port, ConfigData).		% 在这个端口开启监听服务

%% lib_chan_cs manages the connection
%% when a new connection comes the fun which is an 
%% argument to start_raw_server will be called
start_port_server(Port, ConfigData) ->
	lib_chan_cs:start_raw_server(Port,
		fun(Socket) -> start_port_instance(Socket, ConfigData) end, 100, 4).

%% this is spawned when the client connects to the server.
%% Here we setup a middle men,then perform authentication.
%% If everything works,call really_start(Mod,ArgC,{MM, FunC, ArgC})
%% the last three argument come from the configuration file
start_port_instance(Socket, ConfigData) ->
	S = self(),	% S代表服务器进程
	%% Controller是新开进程的Pid，并且它与S链接起来
	Controller = spawn_link(fun() -> start_erl_port_server(S, ConfigData) end),
	lib_chan_mm:loop(Socket, Controller). 

%% MM就是启动服务器的那个Pid(S),这个方法实际上是在接受消息,它知道MM要给我发消息，我也知道我要运行的东西
start_erl_port_server(MM, ConfigData) ->
	receive
		{chan, MM, {startService, Mod, ArgC}} ->	% 这里的Mod就是Service
			case get_service_definition(Mod, ConfigData) of 	% 这里对这个分段函数的匹配是先第二句，后第一句
				{yes, Pwd, MFA} -> 	% MFA = {M, F, A}
					case Pwd of 	% 提取出密码
						none ->
							io:format("none match."),
							send(MM, ack),	%% 发送消息给服务器进程S
							really_start(MM, ArgC, MFA);
						_ -> %% 按照书上的lib_chan使用方法，是会匹配到这个子句的
							io:format("any match,and password=~p~n",[Pwd]),
							do_authentication(Pwd, MM, ArgC, MFA)
					end;
				no ->
					io:format("sending bad service~n"),
					send(MM, badService),
					close(MM)
			end;
		Any ->
			io:format("*** Erl port server got:~p ~p~n",[MM, Any]),
			exit({protocolViolation, Any})
	end.

do_authentication(Pwd, MM, ArgC, MFA) ->
	C = lib_chan_auth:make_challenge(),
	io:format("lib_chan_auth:make_challenge=~p~n",[C]),
	Msg = send(MM, {challenge, C}), 	% 这个对应authentication里的{chan, MM, {challenge, C}}
	io:format("challenge msg:~p~n challenge sender's Pid=~p~n",[Msg, self()]),
	receive
	{chan, MM, {response, R}} ->
		io:format("{chan, MM, {Response, R}}~n"),
		case lib_chan_auth:is_response_correct(C, R, Pwd) of
			true ->
				io:format("response correct?:true~n"),
				send(MM, ack),
				really_start(MM, ArgC, MFA);
			false ->
				io:format("response correct?:false~n"),
				send(MM, authFail),
				close(MM)
		end;
	Any ->
		io:format("Any=~p~n",[Any])
	end.


%% MM is the middle man
%% Mod is the Module we want to execute ArgC and ArgS come from the client and server respectively
really_start(MM, ArgC, {Mod, FunC, ArgS}) -> 
	%% really_start/5的作用就在于这个apply/3
	%% ArgC, ArgS在这里没发挥作用
	case (catch apply(Mod, FunC, [MM, ArgC, ArgS])) of
		{'EXIT', normal} ->
			true;
		{'EXIT', Why} ->
			io:format("server error:~p~n", [Why]);
		Why ->
			io:format("server error should die with exit(normal) was:~p~n", [Why])
	end.

get_service_definition(Mod, [{service, Mod, password, Pwd, mfa, M, F, A} | _]) -> {yes, Pwd, {M, F, A}};
get_service_definition(Name, [ _ | T]) -> get_service_definition(Name, T);
get_service_definition(_, []) -> no.


%% ---------------------------------------------------------------------
%% Client side code
%% connect(...) -> {ok,MM} | Error
%% 你可以通过这也访问远程服务lib_chan:connect("localhost", 2233, math, "123456", {yes,go})
%% math是在配置文件中定义的所要提供的服务,Secret就是密码
connect(Host, Port, Service, Secret, ArgC) ->
	S = self(),
	MM = spawn(fun() -> connect(S, Host, Port) end), % 这个MM是具体接收消息的进程Pid
	receive
		{MM, ok} ->
			case authentication(MM, Service, Secret, ArgC) of
				ok -> {ok, MM};	% 会返回这个{ok, MM}
				Error -> Error
			end;
		{MM, Error} ->
			Error
	end.


%% 这个进程会向它的父进程发送消息，接收代码就是connect/5对应的方法,并且，它就在这里监听着客户端发送来的消息
connect(Parent, Host, Port) ->
	case lib_chan_cs:start_raw_client(Host, Port, 4) of
		{ok, Socket} ->
			Parent ! {self() , ok},
			lib_chan_mm:loop(Socket, Parent);
		Error ->
			Parent ! {self() , Error}
	end.


authentication(MM, Service, Secret, ArgC) ->
	send(MM, {startService, Service, ArgC}),	% 这个消息对应着start_erl_port_server里的receive的第一个匹配
	receive
		{chan, MM, ack} ->
			ok;
		{chan, MM, {challenge, C}} ->
			R = lib_chan_auth:make_response(C, Secret),
			io:format("Receive challenge=~p~n", [C]),
			io:format("lib_chan_auth:return_response=~p~n",[R]),
			send(MM, {response, R}),	% 这个对应do_authentication的{chan, MM, {response, R}}
			receive
				{chan, MM, ack} ->
					ok;
				{chan, MM, authFail} ->
					wait_close(MM),
					{error, authFail};
				Other ->
					{error, Other}
			end;
		{chan, MM, badService} ->
			wait_close(MM),
			{error, badService};
		Other ->
			{error, Other}
	end.



wait_close(MM) ->
	receive
		{chan_closed, MM} -> true
	after 5000 ->
		io:format("** error lib_chan~n"), true
	end.


disconnect(MM) -> close(MM).

%% 这个MM从connect/5那里得来，其实是运行connect/5那个进程的子进程(运行connect/3那个进程)的Pid
rpc(MM, Q) ->
	send(MM, Q),
	receive
		{chan, MM, Reply} ->
			Reply
	end.


cast(MM, Q) -> send(MM, Q).




